using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace AnimationPage.ViewModels;

public partial class SettingsViewModel : ViewModelBase
{
    /// <summary>
    /// The text to show while loading text
    /// </summary>
    private string mLoadingText = "...";

    /// <summary>
    /// The current users first name
    /// </summary>
    [ObservableProperty]
    private TextEntryViewModel _nickName;

    /// <summary>
    /// The current users username
    /// </summary>
    [ObservableProperty]
    private TextEntryViewModel _username;

    /// <summary>
    /// The current users password
    /// </summary>
    [ObservableProperty]
    private PasswordEntryViewModel _password;

    /// <summary>
    /// The current users email
    /// </summary>
    [ObservableProperty]
    private TextEntryViewModel _email;

    /// <summary>
    /// The text for the logout button
    /// </summary>
    [ObservableProperty]
    private string _logoutButtonText;

    /// <summary>
    /// Indicates if the first name is current being saved
    /// </summary>
    public bool FirstNameIsSaving { get; set; }

    /// <summary>
    /// Indicates if the last name is current being saved
    /// </summary>
    public bool LastNameIsSaving { get; set; }

    /// <summary>
    /// Indicates if the username is current being saved
    /// </summary>
    public bool UsernameIsSaving { get; set; }

    /// <summary>
    /// Indicates if the email is current being saved
    /// </summary>
    public bool EmailIsSaving { get; set; }

    /// <summary>
    /// Indicates if the password is current being changed
    /// </summary>
    public bool PasswordIsChanging { get; set; }

    /// <summary>
    /// Indicates if the settings details are currently being loaded
    /// </summary>
    public bool SettingsLoading { get; set; }

    /// <summary>
    /// Indicates if the user is currently logging out
    /// </summary>
    public bool LoggingOut { get; set; }

    /// <summary>
    /// Default constructor
    /// </summary>
    public SettingsViewModel()
    {
        // Create First Name
        NickName = new TextEntryViewModel
        {
            Label = "别名",
            OriginalText = mLoadingText,
            CommitAction = OnSaveFirstNameAsync
        };

        // Create Username
        Username = new TextEntryViewModel
        {
            Label = "账号",
            OriginalText = mLoadingText,
            CommitAction = OnSaveUsernameAsync
        };

        // Create Password
        Password = new PasswordEntryViewModel
        {
            // Label = "Password",
            // FakePassword = "********",
            // CommitAction = OnSavePasswordAsync
        };

        // Create Email
        Email = new TextEntryViewModel
        {
            Label = "邮箱",
            OriginalText = mLoadingText,
            CommitAction = OnSaveEmailAsync
        };

        // TODO: Get from localization
        LogoutButtonText = "退出";
    }

    #region Command Methods

    /// <summary>
    /// Open the settings menu
    /// </summary>
    [RelayCommand]
    private void OnOpen()
    {
        // Close settings menu
        //ViewModelApplication.SettingsMenuVisible = true;
    }

    /// <summary>
    /// Closes the settings menu
    /// </summary>
    [RelayCommand]
    private void OnClose()
    {
        // Close settings menu
        //ViewModelApplication.SettingsMenuVisible = false;
    }

    /// <summary>
    /// Logs the user out
    /// </summary>
    [RelayCommand]
    private async Task OnLogoutAsync()
    {
        // Lock this command to ignore any other requests while processing
        await RunCommandAsync(() => LoggingOut, async () =>
        {
            // TODO: Confirm the user wants to logout

            // Clear any user data/cache
            // await ClientDataStore.ClearAllLoginCredentialsAsync();
            //
            // // Clean all application level view models that contain
            // // any information about the current user
            // ClearUserData();
            //
            // // Go to login page
            // ViewModelApplication.GoToPage(ApplicationPage.Login);
        });
    }

    /// <summary>
    /// Clears any data specific to the current user
    /// </summary>
    [RelayCommand]
    private void OnClearUserData()
    {
        // Clear all view models containing the users info

        Username.OriginalText = mLoadingText;
        Email.OriginalText = mLoadingText;
    }

    /// <summary>
    /// Sets the settings view model properties based on the data in the client data store
    /// </summary>
    [RelayCommand]
    public async Task OnLoadAsync()
    {
        // Lock this command to ignore any other requests while processing
        await RunCommandAsync(() => SettingsLoading, async () =>
        {
            // Store single transcient instance of client data store
            // var scopedClientDataStore = ClientDataStore;
            //
            // // Update values from local cache
            // await UpdateValuesFromLocalStoreAsync(scopedClientDataStore);
            //
            // // Get the user token
            // var token = (await scopedClientDataStore.GetLoginCredentialsAsync())?.Token;
            //
            // // If we don't have a token (so we are not logged in...)
            // if (string.IsNullOrEmpty(token))
            //     // Then do nothing more
            //     return;
            //
            // // Load user profile details form server
            // var result = await WebRequests.PostAsync<ApiResponse<UserProfileDetailsApiModel>>(
            //     // Set URL
            //     RouteHelpers.GetAbsoluteRoute(ApiRoutes.GetUserProfile),
            //     // Pass in user Token
            //     bearerToken: token);
            //
            // // If the response has an error...
            // if (await result.HandleErrorIfFailedAsync("Load User Details Failed"))
            //     // We are done
            //     return;
            //
            // // TODO: Should we check if the values are different before saving?
            //
            // // Create data model from the response
            // var dataModel = result.ServerResponse.Response.ToLoginCredentialsDataModel();
            //
            // // Re-add our known token
            // dataModel.Token = token;
            //
            // // Save the new information in the data store
            // await scopedClientDataStore.SaveLoginCredentialsAsync(dataModel);
            //
            // // Update values from local cache
            // await UpdateValuesFromLocalStoreAsync(scopedClientDataStore);
        });
    }

    /// <summary>
    /// Saves the new First Name to the server
    /// </summary>
    /// <returns>Returns true if successful, false otherwise</returns>
    [RelayCommand]
    private async Task<bool> OnSaveFirstNameAsync()
    {
        // Lock this command to ignore any other requests while processing
        return await RunCommandAsync(() => FirstNameIsSaving, async () =>
        {
            return true;
            // Update the First Name value on the server...
            // return await UpdateUserCredentialsValueAsync(
            //     // Display name
            //     "First Name",
            //     // Update the first name
            //     (credentials) => credentials.FirstName,
            //     // To new value
            //     FirstName.OriginalText,
            //     // Set Api model value
            //     (apiModel, value) => apiModel.FirstName = value
            // );
        });
    }

    /// <summary>
    /// Saves the new Last Name to the server
    /// </summary>
    /// <returns>Returns true if successful, false otherwise</returns>
    [RelayCommand]
    private async Task<bool> OnSaveLastNameAsync()
    {
        // Lock this command to ignore any other requests while processing
        return await RunCommandAsync(() => LastNameIsSaving, async () =>
        {
            return true;
            // Update the Last Name value on the server...
            // return await UpdateUserCredentialsValueAsync(
            //     // Display name
            //     "Last Name",
            //     // Update the last name
            //     (credentials) => credentials.LastName,
            //     // To new value
            //     LastName.OriginalText,
            //     // Set Api model value
            //     (apiModel, value) => apiModel.LastName = value
            // );
        });
    }

    /// <summary>
    /// Saves the new Username to the server
    /// </summary>
    /// <returns>Returns true if successful, false otherwise</returns>
    [RelayCommand]
    private async Task<bool> OnSaveUsernameAsync()
    {
        // Lock this command to ignore any other requests while processing
        return await RunCommandAsync(() => UsernameIsSaving, async () =>
        {
            return true;
            // Update the Username value on the server...
            // return await UpdateUserCredentialsValueAsync(
            //     // Display name
            //     "Username",
            //     // Update the first name
            //     (credentials) => credentials.Username,
            //     // To new value
            //     Username.OriginalText,
            //     // Set Api model value
            //     (apiModel, value) => apiModel.Username = value
            // );
        });
    }

    /// <summary>
    /// Saves the new Email to the server
    /// </summary>
    /// <returns>Returns true if successful, false otherwise</returns>
    [RelayCommand]
    private async Task<bool> OnSaveEmailAsync()
    {
        // Lock this command to ignore any other requests while processing
        return await RunCommandAsync(() => EmailIsSaving, async () =>
        {
            // Update the Email value on the server...
            // return await UpdateUserCredentialsValueAsync(
            //     // Display name
            //     "Email",
            //     // Update the email
            //     (credentials) => credentials.Email,
            //     // To new value
            //     Email.OriginalText,
            //     // Set Api model value
            //     (apiModel, value) => apiModel.Email = value
            // );

            return true;
        });
    }


    /// <summary>
    /// Saves the new Password to the server
    /// </summary>
    /// <returns>Returns true if successful, false otherwise</returns>
    [RelayCommand]
    private async Task<bool> OnSavePasswordAsync()
    {
        // Lock this command to ignore any other requests while processing
        return await RunCommandAsync(() => PasswordIsChanging, async () =>
        {
            // Log it
            // Logger.LogDebugSource($"Changing password...");
            //
            // // Get the current known credentials
            // var credentials = await ClientDataStore.GetLoginCredentialsAsync();
            //
            // // Make sure the user has entered the same password
            // if (Password.NewPassword.Unsecure() != Password.ConfirmPassword.Unsecure())
            // {
            //     // Display error
            //     await UI.ShowMessage(new MessageBoxDialogViewModel
            //     {
            //         // TODO: Localize
            //         Title = "Password Mismatch",
            //         Message = "New password and confirm password must match"
            //     });
            //
            //     // Return fail
            //     return false;
            // }
            //
            // // Update the server with the new password
            // var result = await WebRequests.PostAsync<ApiResponse>(
            //     // Set URL
            //     RouteHelpers.GetAbsoluteRoute(ApiRoutes.UpdateUserPassword),
            //     // Create API model
            //     new UpdateUserPasswordApiModel
            //     {
            //         CurrentPassword = Password.CurrentPassword.Unsecure(),
            //         NewPassword = Password.NewPassword.Unsecure()
            //     },
            //     // Pass in user Token
            //     bearerToken: credentials.Token);
            //
            // // If the response has an error...
            // if (await result.HandleErrorIfFailedAsync($"Change Password"))
            // {
            //     // Log it
            //     Logger.LogDebugSource($"Failed to change password. {result.ErrorMessage}");
            //
            //     // Return false
            //     return false;
            // }
            //
            // // Otherwise, we succeeded...
            //
            // // Log it
            // Logger.LogDebugSource($"Successfully changed password");

            // Return successful
            return true;
        });
    }

    #endregion

    #region Private Helper Methods

    /// <summary>
    /// Loads the settings from the local data store and binds them 
    /// to this view model
    /// </summary>
    /// <returns></returns>
    // private async Task UpdateValuesFromLocalStoreAsync(IClientDataStore clientDataStore)
    // {
    //     // Get the stored credentials
    //     var storedCredentials = await clientDataStore.GetLoginCredentialsAsync();
    //
    //     // Set first name
    //     FirstName.OriginalText = storedCredentials?.FirstName;
    //
    //     // Set last name
    //     LastName.OriginalText = storedCredentials?.LastName;
    //
    //     // Set username
    //     Username.OriginalText = storedCredentials?.Username;
    //
    //     // Set email
    //     Email.OriginalText = storedCredentials?.Email;
    // }

    /// <summary>
    /// Updates a specific value from the client data store for the user profile details
    /// and attempts to update the server to match those details.
    /// For example, updating the first name of the user.
    /// </summary>
    /// <param name="displayName">The display name for logging and display purposes of the property we are updating</param>
    /// <param name="propertyToUpdate">The property from the <see cref="LoginCredentialsDataModel"/> to be updated</param>
    /// <param name="newValue">The new value to update the property to</param>
    /// <param name="setApiModel">Sets the correct property in the <see cref="UpdateUserProfileApiModel"/> model that this property maps to</param>
    /// <returns></returns>
    // private async Task<bool> UpdateUserCredentialsValueAsync(string displayName,
    //     Expression<Func<LoginCredentialsDataModel, string>> propertyToUpdate, string newValue,
    //     Action<UpdateUserProfileApiModel, string> setApiModel)
    // {
    //     // Log it
    //     Logger.LogDebugSource($"Saving {displayName}...");
    //
    //     // Get the current known credentials
    //     var credentials = await ClientDataStore.GetLoginCredentialsAsync();
    //
    //     // Get the property to update from the credentials
    //     var toUpdate = propertyToUpdate.GetPropertyValue(credentials);
    //
    //     // Log it
    //     Logger.LogDebugSource($"{displayName} currently {toUpdate}, updating to {newValue}");
    //
    //     // Check if the value is the same. If so...
    //     if (toUpdate == newValue)
    //     {
    //         // Log it
    //         Logger.LogDebugSource($"{displayName} is the same, ignoring");
    //
    //         // Return true
    //         return true;
    //     }
    //
    //     // Set the property
    //     propertyToUpdate.SetPropertyValue(newValue, credentials);
    //
    //     // Create update details
    //     var updateApiModel = new UpdateUserProfileApiModel();
    //
    //     // Ask caller to set appropriate value
    //     setApiModel(updateApiModel, newValue);
    //
    //     // Update the server with the details
    //     var result = await WebRequests.PostAsync<ApiResponse>(
    //         // Set URL
    //         RouteHelpers.GetAbsoluteRoute(ApiRoutes.UpdateUserProfile),
    //         // Pass the Api model
    //         updateApiModel,
    //         // Pass in user Token
    //         bearerToken: credentials.Token);
    //
    //     // If the response has an error...
    //     if (await result.HandleErrorIfFailedAsync($"Update {displayName}"))
    //     {
    //         // Log it
    //         Logger.LogDebugSource($"Failed to update {displayName}. {result.ErrorMessage}");
    //
    //         // Return false
    //         return false;
    //     }
    //
    //     // Log it
    //     Logger.LogDebugSource($"Successfully updated {displayName}. Saving to local database cache...");
    //
    //     // Store the new user credentials the data store
    //     await ClientDataStore.SaveLoginCredentialsAsync(credentials);
    //
    //     // Return successful
    //     return true;
    // }

    #endregion
}